package com.sxt.jt808.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.charset.Charset;
import java.util.Arrays;

import org.springframework.stereotype.Component;

/**
 * 处理字节的常用工具类方法
 * 
 * @author 罗林
 * @Date 2019/12/15 12:31
 * @Version 1.0
 *
 */
@Component
public class BitConverter {
	
	/**
	 * 无参构造函数
	 */
	public BitConverter() {
	}
	
	
	/**
	 * 从源byte[]数组中，截取指定长度的byte[]数组，并转为String
	 * @param data
	 * @param startIndex
	 * @param lenth
	 * @return
	 */
	public String getStringFromBytes(byte[] data, int startIndex, int lenth){
		return this.getStringFromBytes(data, startIndex, lenth, null);
	}
	
	/**
	 * 从源byte[]数组中，截取指定长度的byte[]数组，并转为String
	 * @param data
	 * @param startIndex
	 * @param lenth
	 * @param defaultVal 返回默认值
	 * @return
	 */
	private String getStringFromBytes(byte[] data, int startIndex, int lenth, String defaultVal) {
		try {
//            byte[] tmp = new byte[lenth];
//            System.arraycopy(data, startIndex, tmp, 0, lenth);
			byte[] tmp = this.subBytes(data,  startIndex, lenth); 
            return new String(tmp, "UTF-8");
        } catch (Exception e) {
            //log.error("解析字符串出错:{}", e.getMessage());
            e.printStackTrace();
            return defaultVal;
        }
	}
	
	/**
	 * 从源byte[]数组中，截取指定长度的byte[]数组，并转为int
	 * @param data
	 * @param startIndex
	 * @param length
	 * @return
	 */
	public int getIntFromBytes(byte[] data, int startIndex, int length) {
        return this.getIntFromBytes(data, startIndex, length, 0);
    }
	/**
	 * 从源byte[]数组中，截取指定长度的byte[]数组，并转为int
	 * @param data
	 * @param startIndex
	 * @param length
	 * @param defaultVal 返回默认值
	 * @return
	 */
    private int getIntFromBytes(byte[] data, int startIndex, int length, int defaultVal) {
        try {
            // 字节数大于4,从起始索引开始向后处理4个字节,其余超出部分丢弃
            final int len = length > 4 ? 4 : length;
//            byte[] tmp = new byte[len];
//            System.arraycopy(data, startIndex, tmp, 0, len);
            byte[] tmp = this.subBytes(data,  startIndex, len); 
            return this.byteToInt(tmp);
        } catch (Exception e) {
            //log.error("解析整数出错:{}", e.getMessage());
            e.printStackTrace();
            return defaultVal;
        }
    }
    /**
     * 从源byte[]数组中，截取指定长度的byte[]数组，并转为short
     * @param data
     * @param startIndex
     * @param length
     * @return 
     */
    public short getShortFromBytes(byte[] data, int startIndex, int length) {
        return this.getShortFromBytes(data, startIndex, length, (short) 0);
    }
    
    /**
     * 
     * @param data
     * @param startIndex
     * @param length
     * @param defaultVal
     * @return
     */
	private short getShortFromBytes(byte[] data, int startIndex, int length, short defaultVal) {
		try {
			// 字节数大于4,从起始索引开始向后处理4个字节,其余超出部分丢弃
            final int len = length > 2 ? 2 : length;
			byte[] tmp = this.subBytes(data,  startIndex, len);
			return this.byteToShort(tmp);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return defaultVal;
		}
	}


	/**
	 * 字节数组中截取指定长度数组
	 * @param src byte源数组	
	 * @param begin	截取源byte数组起始位置（0位置有效）
	 * @param count 截取的数据长度
	 * @return
	 */
	public byte[] subBytes(byte[] src, int begin, int count) {
		byte[] bs = new byte[count];
		/**
		 * arraycopy(Object src,int srcPos,Object dest, int destPos,int length);
		 * src:源数组
		 * srcPos:源数组要复制的起始位置
		 * dest:目的数组
		 * destPos:目的数组放置的起始位置
		 * length:要复制的长度
		*/
        System.arraycopy(src, begin, bs, 0, count);
        return bs;

	}
	
	 //=================================BCD 分割线=================================//
	
	/**
     * BCD字节数组===>String
     * 
     * @param bytes
     * @return 十进制字符串
     */
    public String bcd2String(byte[] bytes) {
        StringBuilder temp = new StringBuilder(bytes.length * 2);
        for (int i = 0; i < bytes.length; i++) {
            // 高四位
            temp.append((bytes[i] & 0xf0) >>> 4);
            // 低四位
            temp.append(bytes[i] & 0x0f);
        }
        return temp.toString().substring(0, 1).equalsIgnoreCase("0") ? temp.toString().substring(1) : temp.toString();
    }
 
    /**
     * 字符串==>BCD字节数组
     * 
     * @param str
     * @return BCD字节数组
     */
    public byte[] string2Bcd(String str) {
        // 奇数,前补零
        if ((str.length() & 0x1) == 1) {
            str = "0" + str;
        }

        byte ret[] = new byte[str.length() / 2];
        byte bs[] = str.getBytes();
        for (int i = 0; i < ret.length; i++) {

            byte high = ascII2Bcd(bs[2 * i]);
            byte low = ascII2Bcd(bs[2 * i + 1]);

            // TODO 只遮罩BCD低四位?
            ret[i] = (byte) ((high << 4) | low);
        }
        return ret;
    }

    private byte ascII2Bcd(byte asc) {
        if ((asc >= '0') && (asc <= '9'))
            return (byte) (asc - '0');
        else if ((asc >= 'A') && (asc <= 'F'))
            return (byte) (asc - 'A' + 10);
        else if ((asc >= 'a') && (asc <= 'f'))
            return (byte) (asc - 'a' + 10);
        else
            return (byte) (asc - 48);
    }
	
	
	//=================================分割线=================================//
	
	/**
	 * short转换到字节数组
	 * 
	 * @param number 需要转换的数据。
	 * @return 转换后的字节数组。
	 */
	public byte[] shortToByte(short number) {
		byte[] b = new byte[2];
		for (int i = 1; i >= 0; i--) {
			b[i] = (byte) (number % 256);
			number >>= 8;
		}
		return b;
	}

	/**
	 * 字节到short转换
	 * 
	 * @param b short的字节数组
	 * @return short数值。
	 */
	public short byteToShort(byte[] b) {
		return (short) ((((b[0] & 0xff) << 8) | b[1] & 0xff));
	}

	/**
	 * 	整型转换到字节数组
	 * 
	 * @param number 整形数据。
	 * @return 整形数据的字节数组。
	 */
	public byte[] intToByte(int number) {
		byte[] b = new byte[4];
		for (int i = 3; i >= 0; i--) {
			b[i] = (byte) (number % 256);
			number >>= 8;
		}
		return b;
	}
	
	/**
	 * 把一个整数转为指定位数byte数组
	 * @param number
	 * @return
	 */
	public byte[] intToByte(int number, int length) {
		byte[] result = new byte[length];
		//字节数大于4，从起始索引开始向后处理4个字节，其余超出部分丢弃
		int len = length > 4 ? 4 : length;
		if (len == 1) {
			result[0] = (byte) (number & 0xFF);
		}else if(len == 2) {
			result[0] = (byte) ((number >>> 8) & 0xFF);
			result[1] = (byte) (number & 0xFF);
		}else if(len == 3) {
			result[0] = (byte) ((number >>> 16) & 0xFF);
			result[1] = (byte) ((number >>> 8) & 0xFF);
			result[2] = (byte) (number & 0xFF);
		}else if(len == 4) {
			result[0] = (byte) ((number >>> 24) & 0xFF);
			result[1] = (byte) ((number >>> 16) & 0xFF);
			result[2] = (byte) ((number >>> 8) & 0xFF);
			result[3] = (byte) (number & 0xFF);
		}
		return result;
	}
	
	/**
	 * 字节数组到整型转换
	 * 
	 * @param b 整形数据的字节数组。
	 * @return 字节数组转换成的整形数据。
	 */
//	public int byteToInt(byte[] b) {
//		return ((((b[0] & 0xff) << 24) | ((b[1] & 0xff) << 16) | ((b[2] & 0xff) << 8) | (b[3] & 0xff)));
//	}
	
	/**
	 * 字节数组到整型转换
	 * @param b 		整形数据的字节数组 
	 * 		  length	字节数组长度
	 * @return
	 */
	public int byteToInt(byte[] b) {
		int result;
		if (b.length == 1) {
			//一个byte转化位整形
			result = (int) b[0] & 0xFF;
		} else if (b.length == 2) {
			//把一个2位的数组转化位整形
			int temp0 = b[0] & 0xFF;
			int temp1 = b[1] & 0xFF;
			result = ((temp0 << 8) + temp1);
		} else if (b.length == 3) {
			//把一个3位的数组转化位整形
			int temp0 = b[0] & 0xFF;
			int temp1 = b[1] & 0xFF;
			int temp2 = b[2] & 0xFF;
			result = ((temp0 << 16) + (temp1 << 8) + temp2);
		} else if (b.length == 4) {
			//把一个4位的数组转化位整形
			int temp0 = b[0] & 0xFF;
			int temp1 = b[1] & 0xFF;
			int temp2 = b[2] & 0xFF;
			int temp3 = b[3] & 0xFF;
			result = ((temp0 << 24) + (temp1 << 16) + (temp2 << 8) + temp3);
		}else {
			result = 0;
		}
		return result;
	}
	
	/**
	 * long转换到字节数组
	 * 
	 * @param number 长整形数据。
	 * @return 长整形转换成的字节数组。
	 */
	public byte[] longToByte(long number) {
		byte[] b = new byte[8];
		for (int i = 7; i >= 0; i--) {
			b[i] = (byte) (number % 256);
			number >>= 8;
		}
		return b;
	}
	/**
	 * long转换到指定位数byte数组
	 * 
	 * @param number 长整形数据。
	 * @return 长整形转换成的字节数组。
	 */
	public byte[] longToBytes(long value, int length) {
		//字节数大于8，从起始索引开始向后处理8个字节，其余超出部分丢弃
		int len = length > 8 ? 8 : length;
		byte[] result = new byte[len];
		int temp;
		for (int i = 0; i < len; i++) {
			temp = (len - 1 - i) * 8;
			if (temp == 0) {
				result[i] += (value & 0x0ff);
			} else {
				result[i] += (value >>> temp) & 0x0ff;
			}
		}
		return result;
	}

	/**
	 * 字节数组到整型的转换
	 * 
	 * @param b 长整形字节数组。
	 * @return 长整形数据。
	 */
	public long byteToLong(byte[] b) {
		return ((((long) b[0] & 0xff) << 56) | (((long) b[1] & 0xff) << 48) | (((long) b[2] & 0xff) << 40)
				| (((long) b[3] & 0xff) << 32) | (((long) b[4] & 0xff) << 24) | (((long) b[5] & 0xff) << 16)
				| (((long) b[6] & 0xff) << 8) | ((long) b[7] & 0xff));
	}

	/**
	 * double转换到字节数组
	 * 
	 * @param d 双精度浮点。
	 * @return 双精度浮点的字节数组。
	 */
	public byte[] doubleToByte(double d) {
		byte[] bytes = new byte[8];
		long l = Double.doubleToLongBits(d);
		for (int i = 0; i < bytes.length; i++) {
			bytes[i] = Long.valueOf(l).byteValue();
			l = l >> 8;
		}
		return bytes;
	}

	/**
	 * 字节数组到double转换
	 * 
	 * @param b 双精度浮点字节数组。
	 * @return 双精度浮点数据。
	 */
	public double byteToDouble(byte[] b) {
		long l;
		l = b[0];
		l &= 0xff;
		l |= ((long) b[1] << 8);
		l &= 0xffff;
		l |= ((long) b[2] << 16);
		l &= 0xffffff;
		l |= ((long) b[3] << 24);
		l &= 0xffffffffl;
		l |= ((long) b[4] << 32);
		l &= 0xffffffffffl;

		l |= ((long) b[5] << 40);
		l &= 0xffffffffffffl;
		l |= ((long) b[6] << 48);
		l &= 0xffffffffffffffl;

		l |= ((long) b[7] << 56);

		return Double.longBitsToDouble(l);
	}

	/**
	 * float转换到字节数组
	 * 
	 * @param d 浮点型数据。
	 * @return 浮点型数据转换后的字节数组。
	 */
	public byte[] floatToByte(float d) {
		byte[] bytes = new byte[4];
		int l = Float.floatToIntBits(d);
		for (int i = 0; i < bytes.length; i++) {
			bytes[i] = Integer.valueOf(l).byteValue();
			l = l >> 8;
		}
		return bytes;
	}

	/**
	 * 字节数组到float的转换
	 * 
	 * @param b 浮点型数据字节数组。
	 * @return 浮点型数据。
	 */
	public float byteToFloat(byte[] b) {
		int l;
		l = b[0];
		l &= 0xff;
		l |= ((long) b[1] << 8);
		l &= 0xffff;
		l |= ((long) b[2] << 16);
		l &= 0xffffff;
		l |= ((long) b[3] << 24);
		l &= 0xffffffffl;

		return Float.intBitsToFloat(l);
	}

	/**
	 * 字符串到字节数组转换
	 * 
	 * @param s       字符串。
	 * @param charset 字符编码
	 * @return 字符串按相应字符编码编码后的字节数组。
	 */
	public byte[] stringToByte(String s, Charset charset) {
		return s.getBytes(charset);
	}

	/**
	 * 字节数组带字符串的转换
	 * 
	 * @param b       字符串按指定编码转换的字节数组。
	 * @param charset 字符编码。
	 * @return 字符串。
	 */
	public String byteToString(byte[] b, Charset charset) {
		return new String(b, charset);
	}

	/**
	 *	对象转换成字节数组。
	 * 
	 * @param obj 字节数组。
	 * @return 对象实例相应的序列化后的字节数组。
	 * @throws IOException
	 */
	public byte[] objectToByte(Object obj) throws IOException {
		ByteArrayOutputStream buff = new ByteArrayOutputStream();
		ObjectOutputStream out = new ObjectOutputStream(buff);
		out.writeObject(obj);
		try {
			return buff.toByteArray();
		} finally {
			out.close();
		}
	}

	/**
	 * 序死化字节数组转换成实际对象。
	 * 
	 * @param b 字节数组。
	 * @return 对象。
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public Object byteToObject(byte[] b) throws IOException, ClassNotFoundException {
		ByteArrayInputStream buff = new ByteArrayInputStream(b);
		ObjectInputStream in = new ObjectInputStream(buff);
		Object obj = in.readObject();
		try {
			return obj;
		} finally {
			in.close();
		}
	}

	/**
	 * 比较两个字节的每一个bit位是否相等.
	 * 
	 * @param a 比较的字节.
	 * @param b 比较的字节
	 * @return ture 两个字节每一位都相等,false有至少一位不相等.
	 */
	public boolean equalsBit(byte a, byte b) {
		return Arrays.equals(byteToBitArray(a), byteToBitArray(b));
	}

	/**
	 * 比较两个数组中的每一个字节,两个字节必须二进制字节码每一位都相同才表示两个 byte相同.
	 * 
	 * @param a 比较的字节数组.
	 * @param b 被比较的字节数.
	 * @return ture每一个元素的每一位两个数组都是相等的,false至少有一位不相等.
	 */
	public boolean equalsBit(byte[] a, byte[] b) {
		if (a == b) {
			return true;
		}
		if (a == null || b == null) {
			return false;
		}

		int length = a.length;
		if (b.length != length) {
			return false;
		}

		for (int count = 0; count < a.length; count++) {
			if (!equalsBit(a[count], b[count])) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 返回某个字节的bit组成的字符串.
	 * 
	 * @param b 字节.
	 * @return Bit位组成的字符串.
	 */
	public String bitString(byte b) {
		StringBuilder buff = new StringBuilder();
		boolean[] array = byteToBitArray(b);
		for (int i = 0; i < array.length; i++) {
			buff.append(array[i] ? 1 : 0);
		}
		return buff.toString();
	}

	/**
	 * 计算出给定byte中的每一位,并以一个布尔数组返回. true表示为1,false表示为0.
	 * 
	 * @param b 字节.
	 * @return 指定字节的每一位bit组成的数组.
	 */
	public boolean[] byteToBitArray(byte b) {
		boolean[] buff = new boolean[8];
		int index = 0;
		for (int i = 7; i >= 0; i--) {
			buff[index++] = ((b >>> i) & 1) == 1;
		}
		return buff;
	}

	/**
	 * 返回指定字节中指定bit位,true为1,false为0. 指定的位从0-7,超出将抛出数据越界异常.
	 *
	 * @param b     需要判断的字节.
	 * @param index 字节中指定位.
	 * @return 指定位的值.
	 */
	public boolean byteBitValue(byte b, int index) {
		return byteToBitArray(b)[index];
	}


	/**
	 * 将指定的IP地址转换成字节表示方式. IP数组的每一个数字都不能大于255,否则将抛出IllegalArgumentException异常.
	 *
	 * @param ipNums IP地址数组.
	 * @return IP地址字节表示方式.
	 */
	public byte[] ipAddressBytes(String address) {
		if (address == null || address.length() < 0 || address.length() > 15) {
			throw new IllegalArgumentException("Invalid IP address.");
		}

		final int ipSize = 4;// 最大IP位数
		final char ipSpace = '.';// IP数字的分隔符
		int[] ipNums = new int[ipSize];
		StringBuilder number = new StringBuilder();// 当前操作的数字
		StringBuilder buff = new StringBuilder(address);
		int point = 0;// 当前操作的数字下标,最大到3.
		char currentChar;
		for (int i = 0; i < buff.length(); i++) {
			currentChar = buff.charAt(i);
			if (ipSpace == currentChar) {
				// 当前位置等于最大于序号后,还有字符没有处理表示这是一个错误的IP.
				if (point == ipSize - 1 && buff.length() - (i + 1) > 0) {
					throw new IllegalArgumentException("Invalid IP address.");
				}
				ipNums[point++] = Integer.parseInt(number.toString());
				number.delete(0, number.length());
			} else {
				number.append(currentChar);
			}
		}
		ipNums[point] = Integer.parseInt(number.toString());

		byte[] ipBuff = new byte[ipSize];
		int pointNum = 0;
		for (int i = 0; i < 4; i++) {
			pointNum = Math.abs(ipNums[i]);
			if (pointNum > 255) {
				throw new IllegalArgumentException("Invalid IP address.");
			}
			ipBuff[i] = (byte) (pointNum & 0xff);
		}

		return ipBuff;
	}
}
